import atexit
import os
import shutil
import sys
import tempfile
import threading
import subprocess
import time
import tkinter as tk
from tkinter import filedialog, scrolledtext, messagebox

import requests

from create_plan_search import start_sub_account_url
from tkinter import ttk

current_version = "1.0.1"
update_url = "http://47.112.119.104/zc-kz/version.json"

class Application(tk.Frame):
    def __init__(self, master=None):
        super().__init__(master)
        self.master = master
        self.master.title(f"创建计划-种草-客资(信息&视频流)-当前版本:{current_version}")
        self.red_dot_visible = False  # 初始为隐藏

        self.temp_dir = tempfile.mkdtemp(prefix="update_")
        # atexit.register(self.cleanup)

        # 设置窗口大小
        window_width = 650
        window_height = 460

        # 获取屏幕尺寸以计算布局，使窗口居中
        screen_width = self.master.winfo_screenwidth()
        screen_height = self.master.winfo_screenheight()

        # 计算居中位置
        x_coordinate = int((screen_width / 2) - (window_width / 2))
        y_coordinate = int((screen_height / 2) - (window_height / 2))

        # 设置窗口位置和大小
        self.master.geometry(f"{window_width}x{window_height}+{x_coordinate}+{y_coordinate}")

        self.pack(padx=10, pady=10)
        self.create_widgets()

        threading.Thread(target=self.check_update_init, daemon=True).start()
        # 存储用户选择的文件路径和并发数
        self.file_path = ""
        self.concurrency = 3

    def create_widgets(self):
        # 主容器框架
        main_frame = tk.Frame(self)
        main_frame.pack(fill="both", expand=True, padx=10, pady=10)

        # 内容框架 - 包含除底部进度条外的所有内容
        content_frame = tk.Frame(main_frame)
        content_frame.pack(fill="both", expand=True)

        # 文件选择框
        self.file_frame = tk.Frame(content_frame)
        self.file_frame.pack(fill="x", pady=5)

        tk.Label(self.file_frame, text="选择文件:").pack(side="left")
        self.file_entry = tk.Entry(self.file_frame, width=50)
        self.file_entry.pack(side="left", padx=5)

        self.browse_btn = tk.Button(self.file_frame, text="选择文件", command=self.select_file)
        self.browse_btn.pack(side="left")

        # 并发数量输入框
        self.concurrency_frame = tk.Frame(content_frame)
        self.concurrency_frame.pack(fill="x", pady=5)

        tk.Label(self.concurrency_frame, text="并发数量:").pack(side="left")
        self.concurrency_entry = tk.Entry(self.concurrency_frame, width=10)
        self.concurrency_entry.pack(side="left", padx=5)
        self.concurrency_entry.insert(0, "3")  # 默认值

        # 启动按钮
        self.start_btn = tk.Button(content_frame, text="开始执行", command=self.start_processing)
        self.start_btn.pack(pady=10)

        # 日志信息显示区域
        tk.Label(content_frame, text="操作日志:").pack(anchor="w")
        self.log_area = scrolledtext.ScrolledText(content_frame, height=10, width=70)
        self.log_area.pack(fill="both", expand=True)

        # 默认禁用日志编辑
        self.log_area.config(state="disabled")

        # ===== Template Section =====
        # 模板按钮放在左下角
        template_frame = tk.Frame(content_frame)
        template_frame.pack(side="left", anchor="sw", pady=10)

        tk.Button(template_frame, text="📝 种草模板文件", command=lambda: self.open_template_file(template_name="种草-信息视频流--创建计划-样例.xlsx")).pack(side="left", padx=5)
        tk.Button(template_frame, text="📝 客资模板文件", command=lambda: self.open_template_file(template_name="客资-全站信息流-创建计划-样例.xlsx")).pack(side="left", padx=5)

        # 创建按钮和红点的容器
        btn_frame = tk.Frame(content_frame)
        btn_frame.pack(side="right", anchor="se", pady=10)

        # 创建按钮
        self.update_btn = tk.Button(btn_frame, text="检测更新", command=self.start_update_check)
        self.update_btn.pack(side="left")

        # 创建红点（默认不显示）
        self.red_dot = tk.Label(
            btn_frame,
            text="●",
            fg="red",
            bg=btn_frame.cget("bg"),  # 保持背景一致
            font=("Arial", 12)
        )
        self.red_dot.pack(side="left", padx=(5, 0))

        # 底部框架 - 专门放进度条和状态
        bottom_frame = tk.Frame(main_frame)
        bottom_frame.pack(fill="x", pady=10)

        # 下载进度条和状态 - 居中显示
        self.progress = ttk.Progressbar(
            bottom_frame,
            orient="horizontal",
            length=300,
            mode="determinate"
        )

        self.status_var = tk.StringVar()
        status_label = ttk.Label(
            bottom_frame,
            textvariable=self.status_var,
            foreground="blue"
        )
        status_label.pack(pady=5)

    def start_update_check(self):
        self.update_btn.config(state="disabled")
        self.status_var.set("正在检查更新...")
        self.log("开始检查更新")
        threading.Thread(target=self.check_update, daemon=True).start()

    def check_update(self):
        try:
            response = requests.get(update_url, timeout=10)
            response.raise_for_status()
            update_info = response.json()

            if self.is_new_version(update_info['version']):
                self.master.after(100, lambda: self.prompt_update(update_info))
            else:
                self.master.after(100, self.show_no_update)

        except Exception as e:
            error_msg = f"检查更新失败: {str(e)}"
            self.master.after(100, lambda: self.show_error(error_msg))

    def check_update_init(self):
        try:
            response = requests.get(update_url, timeout=10)
            response.raise_for_status()
            update_info = response.json()

            if self.is_new_version(update_info['version']):
                # 有新版本，显示红点
                self.red_dot_visible = True
                self.red_dot.pack()  # 显示红点
            else:
                # 没有更新，隐藏红点
                self.red_dot_visible = False
                self.red_dot.pack_forget()  # 隐藏红点

        except Exception as e:
            error_msg = f"检查更新失败: {str(e)}"
            self.master.after(100, lambda: self.show_error(error_msg))


    def show_error(self, message):
        self.log(f"更新错误: {message}", "red")
        self.reset_ui()

    def show_no_update(self):
        message = "当前已是最新版本"
        self.log(message)
        messagebox.showinfo("无更新", message, parent=self.master)
        self.reset_ui()

    def update_progress(self, value, downloaded, total_size):
        self.progress["value"] = value
        self.status_var.set(f"下载中... {value:.1f}% " f"({downloaded // 1024}KB/{total_size // 1024}KB)")


    def is_valid_exe(self, file_path):
        """检查文件是否是有效的Windows可执行文件"""
        try:
            # 检查文件大小
            if os.path.getsize(file_path) < 1024:  # 小于1KB的文件不太可能是有效的EXE
                return False
            # 检查文件头
            with open(file_path, 'rb') as f:
                header = f.read(2)
                if header != b'MZ':  # EXE文件的魔数
                    return False

            return True
        except:
            return False

    def download_file(self, exe_url):
        try:
            # 从URL提取文件名
            file_name = exe_url.split('/')[-1].split('?')[0]  # 移除查询参数
            temp_exe = os.path.join(self.temp_dir, file_name)
            self.log(f"下载文件保存到: {temp_exe}")

            response = requests.get(exe_url, stream=True, timeout=30)
            response.raise_for_status()

            total_size = int(response.headers.get('content-length', 0))
            downloaded = 0

            with open(temp_exe, 'wb') as f:
                for chunk in response.iter_content(chunk_size=8192):
                    if chunk:
                        f.write(chunk)
                        downloaded += len(chunk)
                        progress = (downloaded / total_size) * 100 if total_size > 0 else 0
                        self.master.after(10, lambda v=progress: self.update_progress(v, downloaded, total_size))

            # 验证文件完整性
            if 0 < total_size != downloaded:
                error_msg = f"下载文件不完整: {downloaded}/{total_size} bytes"
                self.log(error_msg)
                raise Exception(error_msg)

            # 验证是否是有效的EXE文件
            if not self.is_valid_exe(temp_exe):
                error_msg = "下载的文件不是有效的Windows可执行文件"
                self.log(error_msg)
                raise Exception(error_msg)

            self.log(f"下载完成, 文件大小: {os.path.getsize(temp_exe)} 字节", "green")
            self.master.after(100, lambda: self.prepare_replace(temp_exe))

        except Exception as e:
            error_msg = f"下载失败: {str(e)}"
            self.log(error_msg)
            self.master.after(100, lambda: self.show_error(error_msg))

    def create_replace_script(self, temp_exe_path):
        bat_path = os.path.join(self.temp_dir, "replace.bat")
        current_exe = os.path.realpath(sys.executable)  # 确保指向真实路径

        current_pid = os.getpid()
        self.log(f"当前PID: {current_pid}, EXE: {current_exe}")

        def escape_path(path):
            return path.replace('"', '""').replace('&', '^&').replace('|', '^|')

        current_exe_escaped = escape_path(current_exe)
        temp_exe_path_escaped = escape_path(temp_exe_path)
        temp_dir_escaped = escape_path(self.temp_dir)
        log_file = escape_path(os.path.join(self.temp_dir, "replace_log.txt"))

        script = f"""
                @echo off
                chcp 65001 >nul
                setlocal enabledelayedexpansion

                :: 请求管理员权限
                net session >nul 2>&1
                if %errorLevel% neq 0 (
                    echo Set UAC = CreateObject^("Shell.Application"^) > "%temp%\\getadmin.vbs"
                    echo UAC.ShellExecute "%~s0", "", "", "runas", 1 >> "%temp%\\getadmin.vbs"
                    "%temp%\\getadmin.vbs"
                    exit /b
                )

                :: 关闭当前进程
                taskkill /f /pid {current_pid} >nul 2>&1

                :: 等待进程完全退出
                :waitloop
                timeout /t 8 /nobreak >nul

                :: 获取原文件名和备份文件名
                set CURRENT_EXE="{current_exe_escaped}"
                set BACKUP_EXE="{os.path.splitext(current_exe_escaped)[0]}_backup{os.path.splitext(current_exe_escaped)[1]}"

                :: 删除旧的备份文件（如有）
                if exist "!BACKUP_EXE!" del /f /q "!BACKUP_EXE!"

                :: 尝试重命名原文件为备份文件
                if exist "!CURRENT_EXE!" (
                    echo 正在尝试重命名原文件为备份文件...
                    ren "!CURRENT_EXE!" "{os.path.splitext(os.path.basename(current_exe_escaped))[0]}_backup{os.path.splitext(current_exe_escaped)[1]}" >nul 2>&1
                    if errorlevel 1 (
                        echo 重命名失败，尝试直接删除原文件...
                        del /f /q "!CURRENT_EXE!" >nul 2>&1
                        if errorlevel 1 (
                            echo 删除原文件失败，更新中止！
                            exit /b 1
                        )
                    )
                )

                :: 复制新文件
                echo 正在复制新文件... >> "{log_file}"
                copy /Y "{temp_exe_path_escaped}" "{current_exe_escaped}" >nul 2>&1
                if errorlevel 1 (
                    echo 复制失败，尝试解除文件锁定... >> "{log_file}"
                    :: 尝试解除锁定
                    takeown /f "{current_exe_escaped}" /r /d y >nul 2>&1
                    icacls "{current_exe_escaped}" /grant administrators:F /t >nul 2>&1
                    :: 再次尝试复制
                    copy /Y "{temp_exe_path_escaped}" "{current_exe_escaped}" >> "{log_file}" 2>&1
                    if errorlevel 1 (
                        echo 最终复制失败，错误详情： >> "{log_file}"
                        type "{log_file}"
                        exit /b 1
                    )
                )
                
                timeout /t 2 /nobreak >nul
                :: 启动新程序
                start "" "{current_exe_escaped}"

                :: 自清理
                if exist "!BACKUP_EXE!" del /f /q "!BACKUP_EXE!"
                if exist "%temp%\\getadmin.vbs" del /f /q "%temp%\\getadmin.vbs"
                if exist "{temp_dir_escaped}" rd /s /q "{temp_dir_escaped}" >nul 2>&1
                del "%~f0" >nul 2>&1
                """

        with open(bat_path, 'w', encoding='utf-8-sig') as f:
            f.write(script)

        return bat_path

    def execute_replace_script(self, bat_script):
        """执行替换脚本并退出"""
        try:
            self.log(f"正在执行批处理脚本: {bat_script}")

            # 隐藏命令行窗口
            startupinfo = subprocess.STARTUPINFO()
            startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW
            startupinfo.wShowWindow = 0

            # 使用绝对路径执行批处理
            subprocess.Popen(
                ['cmd.exe', '/C', bat_script],
                shell=False,
                startupinfo=startupinfo,
                creationflags=subprocess.CREATE_NO_WINDOW
            )

            # 给脚本时间启动
            time.sleep(3)
            self.log("退出应用程序以完成更新", "purple")
            sys.exit(0)

        except Exception as e:
            error_msg = f"执行更新脚本失败: {str(e)}"
            self.log(error_msg)
            self.show_error(error_msg)

    def prepare_replace(self, temp_exe):
        self.status_var.set("准备安装更新...")
        self.progress["value"] = 100
        self.log(f"准备替换文件: {temp_exe}")

        bat_script = self.create_replace_script(temp_exe)

        if messagebox.askyesno(
                "更新准备就绪",
                "更新已下载完成，需要重启应用,是否立即重启?",
                parent=self.master
        ):
            self.execute_replace_script(bat_script)


    def start_download(self, exe_url):
        self.progress.pack(pady=10)
        self.status_var.set("正在下载更新...")
        self.progress["value"] = 0
        self.log(f"开始下载更新文件...", "green")
        threading.Thread(target=self.download_file, args=(exe_url,), daemon=True).start()

    def prompt_update(self, update_info):
        answer = messagebox.askyesno(
            "发现新版本",
            f"发现新版本 {update_info['version']}\n\n更新内容:\n{update_info.get('changelog', '')}\n\n是否立即更新?",
            parent=self.master
        )

        if answer:
            self.start_download(update_info['exe_url'])
        else:
            self.reset_ui()

    def is_new_version(self, remote_version):
        result = remote_version != current_version
        self.log(f"版本比较: 当前={current_version}, 最新版本={remote_version}", "purple")
        return result
    def reset_ui(self):
        self.progress.pack_forget()
        self.progress["value"] = 0
        self.status_var.set("")
        self.update_btn.config(state="normal")

    def cleanup(self):
        try:
            if os.path.exists(self.temp_dir):
                shutil.rmtree(self.temp_dir, ignore_errors=True)
        except Exception as e:
            self.log(f"清理失败: {str(e)}", "red")

    def open_template_file(self, template_name='种草-信息视频流--创建计划-样例.xlsx'):
        """点击按钮后打开模板 Excel 文件"""
        # 构建模板路径
        if getattr(sys, 'frozen', False):  # 如果是打包后的程序
            base_path = sys._MEIPASS
            full_path = os.path.join(base_path, "shared", template_name)
        else:  # 开发环境
            full_path = os.path.abspath(os.path.join("..", "shared", template_name))

        # 检查文件是否存在
        if not os.path.exists(full_path):
            self.log(f"错误: 未找到模板文件 {full_path}", "red")
            return

        # 使用默认程序打开 Excel 文件
        try:
            if os.name == 'nt':  # Windows
                os.startfile(full_path)
            else:  # macOS / Linux
                import subprocess
                if sys.platform == 'darwin':
                    subprocess.call(['open', full_path])  # macOS
                else:
                    subprocess.call(['xdg-open', full_path])  # Linux
        except Exception as e:
            self.log(f"❌ 打开模板文件失败: {str(e)}", "red")

    def select_file(self):
        """打开文件选择对话框"""
        file_path = filedialog.askopenfilename(
            title="选择文件",
            filetypes=[("Excel文件", "*.xlsx")]
        )
        if file_path:
            self.file_path = file_path
            self.file_entry.delete(0, tk.END)
            self.file_entry.insert(0, file_path)
            self.log(f"已选择文件: {file_path}")

    def log(self, message, color="black"):
        """向日志区域添加消息"""
        self.log_area.config(state="normal")

        # 创建或复用标签
        tag_name = f"color_{color}"
        self.log_area.tag_configure(tag_name, foreground=color)

        self.log_area.insert(tk.END, message + "\n", tag_name)
        self.log_area.config(state="disabled")
        self.log_area.see(tk.END)  # 自动滚动到底部

    def start_processing(self):
        """启动处理按钮的回调函数"""
        # 获取用户输入
        self.file_path = self.file_entry.get()
        try:
            self.concurrency = int(self.concurrency_entry.get())
            if self.concurrency <= 0:
                raise ValueError("并发数必须大于0")
        except ValueError as e:
            self.log(f"错误: 并发数量必须是正整数 ({e})")
            return
        if not self.file_path:
            self.log("错误: 请先选择文件")
            return

        self.log(f"开始处理 - 文件: {self.file_path}, 并发数: {self.concurrency}")

        # start_sub_account_url(self.file_path, self.concurrency)
        thread = threading.Thread(
            target=start_sub_account_url,
            args=(self.file_path, self.concurrency),
            kwargs={'log_callback': self.log}  # 传递前端方法
        )
        thread.start()

        # 这里调用你的处理函数，例如:
        # process_file(self.file_path, self.concurrency)
        # 为了演示，我们模拟一个处理过程
        # self.simulate_processing()

if __name__ == "__main__":
    root = tk.Tk()
    app = Application(master=root)
    app.mainloop()